/*
 *
 * Copyright (c) 2020-2022, Java知识图谱 (http://www.altitude.xin).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package xin.altitude.cms.common.util;


import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
import com.baomidou.mybatisplus.core.toolkit.support.LambdaMeta;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import org.apache.ibatis.reflection.property.PropertyNamer;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import static xin.altitude.cms.common.support.FieldFilterSupport.filter;

/**
 * @author <a href="http://www.altitude.xin" target="_blank">Java知识图谱</a>
 * @author <a href="https://gitee.com/decsa/ucode-cms-vue" target="_blank">UCode CMS</a>
 * @author <a href="https://space.bilibili.com/1936685014" target="_blank">B站视频</a>
 **/
public class FieldFilterUtils {
    private FieldFilterUtils() {
    }

    @SafeVarargs
    public static <T, R> void removeFields(List<T> sources, final SFunction<? super T, ? extends R>... action) {
        if (action == null) {
            return;
        }
        sources.forEach(e -> removeFields(e, action));
    }

    public static <T, R> void filterFields(List<T> sources, boolean isInclude, String... fieldNames) {
        sources.forEach(e -> filterFields(e, isInclude, fieldNames));
    }

    @SafeVarargs
    public static <T, R> void removeFields(IPage<T> sources, final SFunction<? super T, ? extends R>... action) {
        if (action == null) {
            return;
        }
        sources.getRecords().forEach(e -> removeFields(e, action));
    }

    @SafeVarargs
    public static <T, R> void removeFields(T source, final SFunction<? super T, ? extends R>... action) {
        if (action == null) {
            return;
        }
        Class<?> currentClass = source.getClass();
        Class<?> superclass = currentClass.getSuperclass();
        List<String> fieldNames = Arrays.stream(action).map(LambdaUtils::extract).map(LambdaMeta::getImplMethodName).map(PropertyNamer::methodToProperty).collect(Collectors.toList());

        for (Field field : currentClass.getDeclaredFields()) {
            for (String fieldName : fieldNames) {
                doRemoveField(source, fieldName, field);
            }
        }

        for (Field field : superclass.getDeclaredFields()) {
            for (String fieldName : fieldNames) {
                doRemoveField(source, fieldName, field);
            }
        }
    }

    public static <T, R> void filterFields(T source, boolean isInclude, String... fieldNames) {
        Class<?> currentClass = source.getClass();
        Class<?> superclass = currentClass.getSuperclass();
        Set<String> set = Arrays.stream(fieldNames).collect(Collectors.toSet());
        if (isInclude) {
            Arrays.stream(currentClass.getDeclaredFields()).filter(e -> !set.contains(e.getName())).forEach(e -> doRemoveField(source, e));
            Arrays.stream(superclass.getDeclaredFields()).filter(e -> !set.contains(e.getName())).forEach(e -> doRemoveField(source, e));

        } else {
            Arrays.stream(currentClass.getDeclaredFields()).filter(e -> set.contains(e.getName())).forEach(e -> doRemoveField(source, e));
            Arrays.stream(superclass.getDeclaredFields()).filter(e -> set.contains(e.getName())).forEach(e -> doRemoveField(source, e));
        }

    }


    /**
     * 过滤属性 操作列表对象 默认情况为排除属性（字段）
     *
     * @param data   泛型列表集合实例
     * @param action 待处理的列字段（用方法引用表示）
     * @param <T>    目标对象泛型
     * @param <R>    方法引用泛型
     * @return JSONObject集合实例
     */
    @SafeVarargs
    public static <T, R> List<JSONObject> filterFields(List<T> data, final SFunction<T, R>... action) {
        return filterFields(data, false, action);
    }


    /**
     * 过滤属性 操作列表对象
     *
     * @param data      泛型列表集合实例
     * @param isInclude 如果是true代表保留字段、false代表排除字段
     * @param action    待处理的列字段（用方法引用表示）
     * @param <T>       目标对象泛型
     * @param <R>       方法引用泛型
     * @return JSONObject集合实例
     */
    @SafeVarargs
    public static <T, R> List<JSONObject> filterFields(List<T> data, boolean isInclude, final SFunction<T, R>... action) {
        if (data == null) {
            return null;
        }
        return data.stream().map(e -> filterFields(e, isInclude, action)).collect(Collectors.toList());
    }

    @SafeVarargs
    public static <T, R> IPage<JSONObject> filterFields(IPage<T> page, final SFunction<T, R>... action) {
        return filterFields(page, false, action);
    }

    /**
     * 过滤属性 操作分页对象
     *
     * @param page      泛型分页实例
     * @param isInclude 如果是true代表保留字段、false代表排除字段
     * @param action    待处理的列字段（用方法引用表示）
     * @param <T>       目标对象泛型
     * @param <R>       方法引用泛型
     * @return JSONObject分页实例
     */
    @SafeVarargs
    public static <T, R> IPage<JSONObject> filterFields(IPage<T> page, boolean isInclude, final SFunction<T, R>... action) {
        if (page == null) {
            return null;
        }
        return EntityUtils.toPage(page, e -> filterFields(e, isInclude, action));
    }

    /**
     * 过滤属性 操作单个对象 默认情况为排除属性（字段）
     *
     * @param obj    泛型对象实例
     * @param action 待处理的列字段（用方法引用表示）
     * @param <T>    目标对象泛型
     * @param <R>    方法引用泛型
     * @return JSONObject实例
     */
    @SafeVarargs
    public static <T, R> JSONObject filterFields(T obj, final SFunction<T, R>... action) {
        return filterFields(obj, false, action);
    }

    /**
     * 过滤属性 操作单个对象
     *
     * @param obj       泛型对象实例
     * @param isInclude 如果是true代表保留字段、false代表排除字段
     * @param action    待处理的列字段（用方法引用表示）
     * @param <T>       目标对象泛型
     * @param <R>       方法引用泛型
     * @return JSONObject实例
     */
    @SafeVarargs
    public static <T, R> JSONObject filterFields(T obj, boolean isInclude, final SFunction<T, R>... action) {
        if (obj == null) {
            return null;
        }
        // String collect = Arrays.stream(action).map(LambdaUtils::extract).map(LambdaMeta::getImplMethodName).map(PropertyNamer::methodToProperty).collect(Collectors.joining(","));
        String fieldNames = RefUtils.getFiledNames(action).stream().collect(Collectors.joining(","));
        return isInclude ? filter(obj, fieldNames, null) : filter(obj, null, fieldNames);
    }


    /**
     * 具体执行删除属性操作 本质是将属性赋null
     *
     * @param source
     * @param fieldName
     * @param field
     * @param <T>
     */
    private static <T> void doRemoveField(T source, String fieldName, Field field) {
        if (field.getName().equals(fieldName)) {
            try {
                field.setAccessible(true);
                field.set(source, null);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    private static <T> void doRemoveField(T source, Field field) {
        try {
            field.setAccessible(true);
            field.set(source, null);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


